#ifndef OMG_DDS_RPC_REQUEST_REPLY_H
#define OMG_DDS_RPC_REQUEST_REPLY_H

#include <string>
#include "dds.h"

namespace dds {
  namespace rpc {

using namespace dds::domain;
using namespace dds::sub;
using namespace dds::pub;
using namespace dds;

class RequestIdentity;
class RequesterParams;
class ServiceParams;

template <class TReq, class TRep>
class Requester {
public:
  // Creates a Requester with the minimum set of parameters. 
  Requester(DomainParticipant * participant,
    const std::string & service_name);

  Requester(DomainParticipant * participant,
    const std::string & service_name,
    const std::string & instance_name);

  // Creates a Requester with parameters. 
  Requester(const RequesterParams &params);

  void call(TRep &, const TReq &, const Duration &);

  void call(Sample<TRep> &, const TReq &, const Duration &);

  LoanedSamples<TRep> call(const TReq &, const Duration &);

  dds::future<Sample<TRep> > call_async(const TReq &);

  void call_oneway(const TReq &);

  void setRequestDataWriterQos(const DataWriterQos & dwqos);

  void setReplyDataReaderQos(const DataReaderQos & drqos);

  void wait_for_replier(unsigned howmany, const Duration &);

  bool bind(const std::string & instance_name);

  bool unbind();

  bool is_bound();

  void get_service_info(std::string & service_name, std::string & bound_instance);
};

template <class TReq, class TRep>
class Service {
public:
  // Creates a Service with the minimum set of parameters. 
  Service(DomainParticipant * participant,
    const std::string & service_name,
    const std::string & instance_name);

  // Creates a Service with parameters. 
  Service(const ServiceParams &params);
  // blocking take
  void receive(TReq &, RequestIdentity &, const Duration &);
  // blocking take with sampleinfo
  void receive(Sample<TReq> &, RequestIdentity &, const Duration &);
  // blocking take
  // SampleInfo will contain the request identity.
  LoanedSamples<TReq> receive(const Duration &);
  bool wait(const Duration &);
  // non-blocking take
  bool take_request(TReq &, RequestIdentity &);
  // non-blocking take with sampleinfo
  bool take_request(Sample<TReq> &, RequestIdentity &);
  // read-blocking take
  bool read_request(TReq &, RequestIdentity &);
  // non-blocking take with sampleinfo
  bool read_request(Sample<TReq> &, RequestIdentity &);
  bool reply(const TRep &, const RequestIdentity &);
  void setReplyDataWriterQos(const DataWriterQos & dwqos);

  void setRequestDataReaderQos(const DataReaderQos & drqos);
};

template <class TReq, class TRep>
class ServiceListener {
public:
  virtual TRep * process_request(const Sample<TReq> &, const RequestIdentity &) = 0;
  ~ServiceListener();
};

template <class TReq, class TRep>
class AsyncServiceListener {
public:
  virtual void on_request_available(Service<TReq, TRep> &) = 0;
  ~AsyncServiceListener();
};

template <class TRep>
class ReplyListener {
public:
  virtual void process_reply(const Sample<TRep> &, const RequestIdentity &) = 0;
  ~ReplyListener();
};

template <class TReq, class TRep>
class AsyncReplyListener {
public:
  virtual void on_reply_available(Requester<TReq, TRep> &) = 0;
  ~AsyncReplyListener();
};

class RequesterParams {
public:
  RequesterParams(DomainParticipant *participant);

  template <class TRep>
  RequesterParams & 	reply_listener(ReplyListener<TRep> &listener);

  template <class TReq, class TRep>
  RequesterParams & 	async_reply_listener(AsyncReplyListener<TReq, TRep> &listener);

  RequesterParams & 	service_name(const std::string &name);
  RequesterParams & 	request_topic_name(const std::string &name);
  RequesterParams & 	reply_topic_name(const std::string &name);
  RequesterParams & 	datawriter_qos(const DataWriterQos &qos);
  RequesterParams & 	datareader_qos(const DataReaderQos &qos);
  RequesterParams & 	publisher(Publisher *publisher);
  RequesterParams & 	subscriber(Subscriber *subscriber);
};

class ServiceParams {
public:
  ServiceParams(DomainParticipant *participant);

  template <class TReq, class TRep>
  ServiceParams & 	service_listener(ServiceListener< TReq, TRep > &listener);

  template <class TReq, class TRep>
  ServiceParams & 	async_service_listener(AsyncServiceListener< TReq, TRep > &listener);

  ServiceParams & 	service_name(const std::string &service_name);
  ServiceParams & 	request_topic_name(const std::string &req_topic);
  ServiceParams & 	reply_topic_name(const std::string &rep_topic);
  ServiceParams & 	datawriter_qos(const DataWriterQos &qos);
  ServiceParams & 	datareader_qos(const DataReaderQos &qos);
  ServiceParams & 	publisher(Publisher *publisher);
  ServiceParams & 	subscriber(Subscriber *subscriber);
};

template <class T>
class future {
  // mimic std::future in C++11 to the extent possible in C++03.
public:
  T get();
  void wait();
  void wait(const Duration &);
};

} // namespace rpc
} // namespace dds

#endif // OMG_DDS_RPC_REQUEST_REPLY_H
